#include "socket.h"
#include "socket_p.h"
#include <arpa/inet.h>
#include <assert.h>
#include <sys/time.h>

static inline timeval time()
{
    struct timeval time;
    gettimeofday(&time, 0);
    return time;
}

static inline int msecsSince(const timeval &t)
{
    const timeval now = time();
    int ret = (now.tv_sec - t.tv_sec) * 1000;
    ret += (now.tv_usec - t.tv_usec) / 1000;
    return ret;
}

void Socket::sendPacket(const char *data, int size)
{
    const uint32_t networkSize = htonl(size);
    int ret = write((const char*)&networkSize, sizeof(uint32_t));
    assert(ret == sizeof(uint32_t));

    ret = write(data, size);
    assert(ret == size);
}

int Socket::receivePacket(char *buffer, int maxSize, int timeout_msecs)
{
    timeval tv;
    if (timeout_msecs > 0)
        tv = ::time();

    while (bytesAvailable() < int(sizeof(uint32_t))) {
        waitForBytesAvailable(sizeof(uint32_t), timeout_msecs);
        if (timeout_msecs > 0 && msecsSince(tv) > timeout_msecs)
            return -1;
    }
    assert(bytesAvailable() >= int(sizeof(uint32_t)));
    uint32_t packetSize;
    int ret = read((char *)&packetSize, sizeof(uint32_t));
    assert(ret == sizeof(uint32_t));
    packetSize = ntohl(packetSize);

    if (timeout_msecs > 0) {
        timeout_msecs -= msecsSince(tv);
        if (timeout_msecs <= 0) { // ### need to put bytes back on wire. Hm.
            return -1;
        }
    }

    if (int(packetSize) > maxSize) {
        return -1; // ### need to put bytes back on wire. Hm.
    }

    while (bytesAvailable() < int(packetSize)) {
        waitForBytesAvailable(packetSize, timeout_msecs);
        if (timeout_msecs > 0 && msecsSince(tv) > timeout_msecs)
            return -1;
    }
    assert(bytesAvailable() >= int(packetSize));
    ret = read(buffer, packetSize);
    assert(ret = packetSize);
    assert(int(packetSize) < maxSize);
    return packetSize;
}

std::string Socket::server() const
{
    return data->server;
}

int Socket::port() const
{
    return data->port;
}

const std::list<Socket*> &Server::connections() const
{
    return data->connections;
}
